#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/err.h>

struct led_test_data {
    struct gpio_desc *led_gpio;
};

static struct led_test_data *led_data;

/* LED 控制函数 */
static ssize_t led_test_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    char value;

    if (copy_from_user(&value, buf, 1))
        return -EFAULT;

    if (value == '1') {
        gpiod_set_value(led_data->led_gpio, 1);
        pr_info("LED turned on\n");
    } else if (value == '0') {
        gpiod_set_value(led_data->led_gpio, 0);
        pr_info("LED turned off\n");
    } else {
        pr_err("Invalid value, use '1' to turn on and '0' to turn off\n");
        return -EINVAL;
    }

    return count;
}

static const struct file_operations led_test_fops = {
    .owner = THIS_MODULE,
    .write = led_test_write,
};

static struct miscdevice led_misc_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "led_test",
    .fops = &led_test_fops,
};

static int led_test_probe(struct platform_device *pdev)
{
    int ret;

    /* 分配设备数据结构 */
    led_data = devm_kzalloc(&pdev->dev, sizeof(*led_data), GFP_KERNEL);
    if (!led_data)
        return -ENOMEM;

    /* 获取设备树中的 GPIO 描述符 */
    led_data->led_gpio = devm_gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW);
    if (IS_ERR(led_data->led_gpio)) {
        dev_err(&pdev->dev, "Failed to get GPIO for LED\n");
        return PTR_ERR(led_data->led_gpio);
    }

    /* 设置默认状态 */
    gpiod_set_value(led_data->led_gpio, 0);

    /* 注册 misc 设备 */
    ret = misc_register(&led_misc_device);
    if (ret) {
        dev_err(&pdev->dev, "Failed to register misc device\n");
        return ret;
    }

    dev_info(&pdev->dev, "LED test driver probed successfully\n");
    return 0;
}

static int led_test_remove(struct platform_device *pdev)
{
    /* 注销 misc 设备 */
    misc_deregister(&led_misc_device);

    /* 确保 LED 关闭 */
    gpiod_set_value(led_data->led_gpio, 0);
    dev_info(&pdev->dev, "LED test driver removed\n");
    return 0;
}

static const struct of_device_id led_test_of_match[] = {
    { .compatible = "led0-test" },
    {},
};
MODULE_DEVICE_TABLE(of, led_test_of_match);

static struct platform_driver led_test_driver = {
    .probe = led_test_probe,
    .remove = led_test_remove,
    .driver = {
        .name = "led_test",
        .of_match_table = led_test_of_match,
    },
};

module_platform_driver(led_test_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("LED Test Driver using GPIO Subsystem");
